package org.raftkv.raft.core.status;


import org.raftkv.raft.core.Raft;
import org.raftkv.raft.core.enums.StateType;
import org.raftkv.raft.tracker.Config;
import org.raftkv.raft.tracker.Progress;

import java.util.HashMap;
import java.util.Map;

public class Status {

    private BasicStatus basicStatus;

    private Map<Long, Progress> progress;

    private Config config;

    public static Map<Long, Progress> getProgressCopy(Raft r) {
        Map<Long, Progress> m = new HashMap<>();
        r.getPrs().visit((id, prs) -> {
            Progress pr = prs.getProgressMap().get(id);
            Progress p = pr.clone();
            p.setInflights(pr.getInflights().clone());
            pr = null;

            m.put(id, p);
        });
        return m;
    }

    public static BasicStatus getBasicStatus(Raft r) {
        BasicStatus s = new BasicStatus();
        s.setHardState(r.hardState());
        s.setSoftState(r.softState());
        s.setApplied(r.getRaftLog().getApplied());
        return s;
    }

    public static Status getStatus(Raft r) {
        Status s = new Status();
        s.basicStatus = getBasicStatus(r);
        if (s.basicStatus.getSoftState().getRaftState() == StateType.StateLeader) {
            s.setProgress(getProgressCopy(r));
        }
        s.setConfig(r.getPrs().getConfig().clone());
        return s;
    }

    public byte[] marshalJSON() {
        String j = String.format("{\"id\":\"%x\",\"term\":%d,\"vote\":\"%x\",\"commit\":%d,\"lead\":\"%x\",\"raftState\":%s,\"applied\":%d,\"progress\":{",
                this.basicStatus.getId(), this.basicStatus.getHardState().getTerm(), this.basicStatus.getHardState().getVote(),
                this.basicStatus.getHardState().getCommit(), this.basicStatus.getSoftState().getLead(),
                this.basicStatus.getSoftState().getRaftState(), this.basicStatus.getApplied());
        if (this.getProgress().size() == 0) {
            j += "},";
        } else {
            for (Map.Entry<Long, Progress> entry : this.getProgress().entrySet()) {
                Long k = entry.getKey();
                Progress v = entry.getValue();
                String subj = String.format("\"%x\":{\"match\":%d,\"next\":%d,\"state\":%s},",
                        k, v.getMatch(), v.getNext(), v.getState());
                j += subj;
            }
            j = j.substring(0, j.length() - 1) + "},";
        }

        j += String.format("\"leadTransferee\":\"%x\"}", this.basicStatus.getLeadTransferee());
        return j.getBytes();
    }

    public Status() {

    }

    @Override
    public String toString() {
        return new String(this.marshalJSON());
    }

    public Map<Long, Progress> getProgress() {
        return progress;
    }

    public void setProgress(Map<Long, Progress> progress) {
        this.progress = progress;
    }

    public Config getConfig() {
        return config;
    }

    public void setConfig(Config config) {
        this.config = config;
    }

}
